﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using Windows.Storage;
using System.IO;
using System.Runtime.Serialization.Json;

namespace DataBase
{
    /// <summary>
    /// fakedatabase
    /// </summary>
    public abstract class FakeDataBase
    {
        private string DBAddress;

        public FakeDataBase(string dbAddress)
        {
            this.DBAddress = dbAddress;
        }

        private bool isInitialed;
        /// <summary>
        /// initail database
        /// </summary>
        /// <returns></returns>
        public virtual async Task InitialBatabaseAsync()
        {
            if (!isInitialed)
            {
                Type t = this.GetType();
                foreach (var item in t.GetTypeInfo().DeclaredFields)
                {
                    if (item.FieldType.Name == "Table`1")
                    {
                        string address = DBAddress + "_" + item.Name;
                        var value = await DeserializeObjectFromFile(address, item.FieldType);
                        if (value == null)
                        {
                            item.SetValue(this, CreateInstance(item.FieldType, null));
                        }
                        else
                            item.SetValue(this, value);
                    }
                }
                isInitialed = true;
            }
            
        }

        /// <summary>
        /// submission
        /// </summary>
        /// <returns></returns>
        public virtual async Task SubmitChanges()
        {
            Type t = this.GetType();
            foreach (var item in t.GetTypeInfo().DeclaredFields)
            {
                var itemType = item.FieldType;
                if (itemType.Name == "Table`1")
                {
                    var info = itemType.GetTypeInfo().DeclaredFields.FirstOrDefault(c => c.Name == "IsChanged");
                    var table = item.GetValue(this);
                    var value = (bool)info.GetValue(table);
                    if (value)
                    {
                        string address = DBAddress + "_" + item.Name;
                        await SerializeObjectToFile(address, item.GetValue(this));
                        info.SetValue(table, false);
                    }
                }
            }
        }


        /// <summary>
        /// DeserializeObjectFromFile
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        private async Task<object> DeserializeObjectFromFile(string fileName, Type type)
        {
            StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
            StorageFile file = null;
            object objTarget;
            try
            {
                file = await storageFolder.GetFileAsync(fileName);
            }
            catch (FileNotFoundException)
            {

            }
            if (file == null)
            {
                objTarget = null;
            }
            else
            {
                string str = await FileIO.ReadTextAsync(file);
                objTarget = JsonDeserialize(str, type);
            }
            return objTarget;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="str"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        private object JsonDeserialize(string str, Type type)
        {
            if (string.IsNullOrEmpty(str))
                return null;
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(type);
            using (Stream stream = new MemoryStream(System.Text.UTF8Encoding.UTF8.GetBytes(str)))
            {
                return serializer.ReadObject(stream);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="target"></param>
        /// <returns></returns>
        private static string JsonSerializer(object target)
        {
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(target.GetType());
            using (Stream stream = new MemoryStream())
            {
                serializer.WriteObject(stream, target);
                stream.Seek(0, SeekOrigin.Begin);
                using (StreamReader reader = new StreamReader(stream))
                {
                    return reader.ReadToEnd();
                }
            }
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="target"></param>
        /// <returns></returns>
        private async Task SerializeObjectToFile(string fileName, object target)
        {
            StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
            StorageFile storageFile = null;
            
            if (target == null)
            {
                storageFile = await storageFolder.GetFileAsync(fileName);
                if (storageFile != null)
                {
                    await storageFile.DeleteAsync();
                }
                return;
            }
            string str = JsonSerializer(target);

            storageFile = await storageFolder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
            await FileIO.WriteTextAsync(storageFile, str);
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="t"></param>
        /// <param name="paramas"></param>
        /// <returns></returns>
        private object CreateInstance(Type t, object[] paramas)
        {
            int pCount = paramas == null ? 0 : paramas.Length;
            foreach (var item in t.GetTypeInfo().DeclaredConstructors)
            {
                var p = item.GetParameters();
                if (p.Length == pCount)
                    return item.Invoke(paramas);
            }
            throw new InvalidOperationException("没有找到合适的构造函数");
        }

    }
}
